/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
// #include "at32f413.h"
#include <stdbool.h>
#include "systick.h"
#include <string.h>
#include "bootloader.h"
#include "crc_func_def.h"
#include "CRC32.h"

/**FIXME have to send another 4 bytes to prevent crc error
 * crc error when length > 48bytes
 */
#define SERIAL_BUG 0
#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF
// DEF_CRC_FUNC(crc32_cal, 32, crc32__table);
uint32 crc32_cal(const uint8 *pdata, uint32_t len, uint32 initial, uint32 finalXor, BOOL inputReflected, BOOL resultReflected)
{
    uint32 crc = initial;
    uint32 temp1 = 0, temp2 = 0, pos = 0;
    const uint32 *pTable = crc32__table;
    for (int i = 0; i < len; i++)
    {
        uint32 curByte = pdata[i];
        if (inputReflected)
        {
            curByte = Reflect8(pdata[i]);
        }
        temp1 = (crc ^ (curByte << (32 - 8)));
        pos = (temp1 >> (32 - 8)) & 0xFF;
        temp2 = (temp1 << 8);
        crc = (temp2 ^ pTable[pos]);
    }
    if (resultReflected)
    {
        crc = Reflect32(crc);
    }
    return (crc ^ finalXor);
}
// uint8_t testBuffer[] = {
//     0x40, 0xAC, 0x6F, 0x73, 0x00, 0x38, 0x00, 0x08, 0xAF, 0x50, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00,
//     0xA5, 0x34, 0x12, 0x5A, 0x00, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
//     0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0B, 0xE8, 0x07, 0x00, 0x00, 0x00, 0x00,
//     0x47, 0x00, 0x00, 0x00, 0x40, 0x02, 0x03, 0x00, 0x0F, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
//     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// void crc32_test(bl_info_t *bl)
// {
//     uint32_t crc = crc32_cal(testBuffer + 4, sizeof(testBuffer) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
//     // printf("crc32_test:%08X\r\n", crc);
//     bl_pack_send(bl, CMD_ACK, crc, NULL, 0);
// }
void bl_pack_clear(bl_info_t *bl)
{
    memset(bl->pack.buffer, 0, sizeof(bl->pack.buffer));
    bl->pack.buffer_size = 0;
}
void bl_pack_send(bl_info_t *bl, uint8_t cmd, uint32_t addr, uint8_t *data, uint16_t data_size)
{
    uint32_t crc = 0;
    bl_pack_clear(bl);
    bl->pack.cmd1 = cmd;
    bl->pack.cmd2 = (uint8_t)~cmd;
    bl->pack.addr = addr;
    memcpy(bl->pack.data, data, data_size);
    bl->pack.data_size = data_size;
    bl->pack.buffer_size = 12 + bl->pack.data_size;
    crc = crc32_cal(bl->pack.buffer + 4, bl->pack.buffer_size - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    bl->pack.crc = crc;
    bl->write(bl->pack.buffer, bl->pack.buffer_size);
    bl->write((uint8_t *)&crc, 0); /**FIXME USB-CDC BUG! */
#if (SERIAL_BUG)
    /**FIXME  长度是64的倍数时，会卡住不发送 */

#endif
}
uint32_t bl_pack_recv(bl_info_t *bl)
{
    uint32_t crc = -1;
    bl_pack_clear(bl);
    bl->pack.buffer_size = bl->read(bl->pack.buffer);
    if (bl->pack.buffer_size > 0)
    {
        uint32_t crcLen = bl->pack.buffer_size - 4;
#if (SERIAL_BUG)
        crcLen -= 4;
#endif
        crc = crc32_cal(bl->pack.buffer + 4, crcLen, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
        if (crc == bl->pack.crc)
        {
            return 0;
        }
    }
    return crc;
}
void bl_write_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr <= (bl->appAddr + bl->appSize))
    {
        bl->flashWrite(bl->pack.addr, bl->pack.data, bl->pack.data_size);
        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 51);
    }
}
void bl_read_handle(bl_info_t *bl)
{
    uint8_t readBuffer[FLASH_SectorSize];
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        uint32_t sizeRemain = bl->appAddr + bl->appSize - bl->pack.addr;
        uint32_t readSize = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);
        readSize = readSize > sizeRemain ? sizeRemain : readSize;

        bl->flashRead(bl->pack.addr, readBuffer, readSize);
        bl_pack_send(bl, CMD_ACK, bl->pack.addr, readBuffer, readSize);
        // bl->pack.cmd1 = CMD_ACK;
        // bl->pack.cmd2 = (uint8_t)~CMD_ACK;
        // bl->pack.addr = bl->pack.addr;
        // bl->pack.data_size = readSize;
        // bl->pack.buffer_size = 12 + bl->pack.data_size;
        // bl->pack.crc = crc32_cal(bl->pack.buffer + 4, bl->pack.buffer_size - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
        // bl->write(bl->pack.buffer, bl->pack.buffer_size);
        // delay_ms(2);
        // bl->write((uint8_t *)&bl->pack.crc, 0);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 41);
    }
}

void bl_erase_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        // bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
        uint32_t sizeRemain = bl->appAddr + bl->appSize - bl->pack.addr;
        uint32_t eraseSize = 0;

        eraseSize = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);
        // for (uint8_t i = 0; i < bl->pack.data_size; i++)
        // {
        //     eraseSize <<= 8;
        //     eraseSize |= bl->pack.data[i];
        // }
        eraseSize = eraseSize > sizeRemain ? sizeRemain : eraseSize;
        uint32_t endAddr = bl->pack.addr + eraseSize;
        bl->flashUnlock();
        uint32_t eraseAddr = bl->pack.addr;
        for (; eraseAddr < endAddr; eraseAddr += bl->sectorSize)
        {
            bl_pack_send(bl, CMD_ACK, eraseAddr, (uint8_t *)&endAddr, 4);
            bl->flashSectorErase(eraseAddr);
        }
        bl_pack_send(bl, CMD_ACK, eraseAddr, (uint8_t *)&endAddr, 4);
        bl->flashLock();
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 21);
    }
}

void bl_jump_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        bl_pack_send(bl, CMD_ACK, bl->pack.addr, NULL, 0);
        if (bl->jumpToAddr)
            bl->jumpToAddr(bl->pack.addr);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, bl->appAddr, NULL, 31);
    }
}
void bl_W_fwInfo_handle(bl_info_t *bl)
{
    int err = 0;
    memset(&bl->fw_info, 0, sizeof(firmware_info_t));
    memcpy(&bl->fw_info, bl->pack.data, bl->pack.data_size);
    uint32_t crc = crc32_cal((const uint8_t *)&bl->fw_info + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != bl->fw_info.infoCrc)
    {
        err = 11;
        goto NackExit;
    }
    if (bl->fw_info.magic != FLASH_FW_MAGIC)
    {
        err = 12;
        goto NackExit;
    }
    if (bl->fw_info.size > bl->appSize || bl->fw_info.size == 0)
    {
        err = 13;
        goto NackExit;
    }
    if (bl->fw_info.start_addr < bl->appAddr || bl->fw_info.start_addr >= (bl->appAddr + bl->appSize))
    {
        err = 14;
        goto NackExit;
    }
    if (bl->fw_info.whoAmI.ID != bl->whoAmI.ID || bl->fw_info.whoAmI.Serial != bl->whoAmI.Serial)
    {
        err = 15;
        goto NackExit;
    }
    bl->flashUnlock();
    bl->flashSectorErase(bl->fwInfoAddr);
    bl->flashWrite(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));
    bl->flashLock();
    bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
    return;

NackExit:
    bl_pack_send(bl, CMD_NACK, 0, NULL, err);
}
void bl_R_fwInfo_handle(bl_info_t *bl)
{
    // uint8_t testbuff[1024];
    uint16_t dataSize = bl->pack.addr != 0 ? bl->pack.addr : sizeof(firmware_info_t);
    bl->flashRead(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));
    // for(uint16_t i=0;i<dataSize;i++)
    // {
    //     testbuff[i] = i;
    // }
    // bl->write(&testbuff[960], dataSize-960);
    bl_pack_send(bl, CMD_ACK, bl->fwInfoAddr, (uint8_t *)&bl->fw_info, dataSize);
}
uint32_t bl_check_fw(bl_info_t *bl)
{
    uint32_t crc = 0;

    bl->flashRead(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));
    crc = crc32_cal((uint8_t *)&bl->fw_info + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (bl->fw_info.magic == FLASH_FW_MAGIC && bl->fw_info.size < bl->appSize && crc == bl->fw_info.infoCrc)
    {
        const uint8_t *fwData = (const uint8_t *)bl->fw_info.start_addr;
        if (bl->fw_info.bootTimes == 0)
        {
            /**First time,  check crc*/
            crc = crc32_cal(fwData, bl->fw_info.size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
            if (crc == bl->fw_info.FWcrc)
            {
                bl->jumpToAddr(bl->appAddr);
                return 0;
            }
        }
        else if (bl->fw_info.bootTimes == 0xFFFFFFFF)
        {
            /**system failure*/
            return 0;
        }
        else
        {
            bl->jumpToAddr(bl->appAddr);
            return 0;
        }
    }
    // bl_pack_send(bl, CMD_LOG, bl->fwInfoAddr, (uint8_t *)"fw crc error\r\n", 15);
    return 1;
}

void bootloader_handle(bl_info_t *bl)
{
#if 1
    bl->pack.buffer_size = bl->read(bl->pack.buffer);
    if (bl->pack.buffer_size != 0)
    {
        uint32_t crcLen = bl->pack.buffer_size - 4;
        uint32_t crc = crc32_cal(bl->pack.buffer + 4, crcLen, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
        uint8_t cmd2 = ~bl->pack.cmd2;
        if (bl->pack.crc == crc)
        {
            if (bl->pack.cmd1 == cmd2)
            {
                if(!bl->synecd)
                {
                    if(bl->pack.cmd1 == CMD_SYNC)
                    {
                        bl->synecd = true;
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                    }
                    else
                    {
                        bl_pack_send(bl, CMD_NACK, 0, NULL, 3);
                    }
                }
                else
                {
                    /**NOTE SYNC */
                    switch (bl->pack.cmd1)
                    {
                    case CMD_SYNC:
                        bl->synecd = true;
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                        break;
                    case CMD_VERSION:
                        bl_pack_send(bl, CMD_ACK, 0, (uint8_t *)&bl->version, 4);
                        break;
                    case CMD_GETID:
                        bl_pack_send(bl, CMD_ACK, 0, (uint8_t *)&bl->whoAmI, sizeof(bl->whoAmI));
                        break;
                    case CMD_WRITE:
                        bl_write_handle(bl);
                        break;
                    case CMD_READ:
                        bl_read_handle(bl);
                        break;
                    case CMD_RESET:
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                        if (bl->reset)
                            bl->reset();
                        break;
                    case CMD_JUMP:
                        bl_jump_handle(bl);
                        break;
                    case CMD_ERASE:
                        bl_erase_handle(bl);
                        break;
                    case CMD_W_FW_INFO:
                        bl_W_fwInfo_handle(bl);
                        break;
                    case CMD_R_FW_INFO:
                        bl_R_fwInfo_handle(bl);
                        break;
                    default:
                        /** unknown cmd */
                        bl_pack_send(bl, CMD_NACK, 0, NULL, 1);
                        break;
                    }
                }
            }
            else
            {
                /**NOTE cmd error */
                bl_pack_send(bl, CMD_NACK, 0, NULL, 2);
            }
        }
        else
        {
            /**NOTE crc error */
            bl_pack_send(bl, CMD_NACK, 0, NULL, 3);
        }
        memset(bl->pack.buffer, 0, bl->pack.buffer_size);
        bl->pack.buffer_size = 0;
    }
#endif
    if (bl->synecd == false)
    {
        if (bl->millis() > (bl->dt + 100))
        {
            // NOTE IAP timeout. Exit.
            bl_check_fw(bl);
            bl->dt = bl->millis();
        }
    }
}
